#include "../../../Include/Utils/Tokenizer/WizUtilsTokenizer.hpp"

namespace Wiz
{
    namespace Tokenizer
    {
        Type::Type()
        {
            ::Wiz::Bit::Set(m_Flags, eDefaultSeparators);
        }

        Type::~Type()
        {

        }

        ::Wiz::Bool::Type Type::ProcessQuote(::Wiz::Char::In CurrChar, ParseState& PS, ::Wiz::Size::InOut CurrPos)
        {
            static const ::Wiz::Char::Type Quote = '\"';
            if (PS.IsInQuote)
            {
                if (CurrChar == Quote)
                {
                    PS.IsInQuote = ::Wiz::Bool::False;
                    PS.RecordState = eRecordEnd;
                    return ::Wiz::Bool::True;
                }
                return ::Wiz::Bool::True;
            }
            else if (CurrChar == Quote)
            {
                if (PS.RecordState == eRecording)
                {
                    PS.RecordState = eRecordEnd;
                }
                PS.IsInQuote = ::Wiz::Bool::True;
                return ::Wiz::Bool::True;
            }
            return ::Wiz::Bool::False;
        }
        ::Wiz::Bool::Type Type::ProcessDefaultSeparator(::Wiz::Char::In CurrChar, ParseState& PS, ::Wiz::Size::InOut CurrPos)
        {
            if (::Wiz::IsSpace(CurrChar))
            {
                if (PS.RecordState == eRecording)
                {
                    PS.RecordState = eRecordEnd;
                    return ::Wiz::Bool::True;
                }
            }
            return ::Wiz::Bool::False;
        }
        ::Wiz::Bool::Type Type::ProcessSeparator(::Wiz::Char::In CurrChar, ParseState& PS, ::Wiz::Size::InOut CurrPos)
        {
            if (IsSeparator(CurrChar))
            {
                if (PS.RecordState == eRecording)
                {
                    PS.RecordState = eRecordEnd;
                    return ::Wiz::Bool::True;
                }
            }
            return ::Wiz::Bool::False;
        }
        ::Wiz::Bool::Type Type::ProcessBracket(::Wiz::Char::In CurrChar, ParseState& PS, ::Wiz::Size::InOut CurrPos)
        {
            /// square brackets []
            static const ::Wiz::Char::Type OpenBracket = '[';
            static const ::Wiz::Char::Type CloseBracket = ']';
            /// parentheses ()
            static const ::Wiz::Char::Type OpenParen = '(';
            static const ::Wiz::Char::Type CloseParen = ')';
            /// Angle brackets <>
            static const ::Wiz::Char::Type OpenAngle = '<';
            static const ::Wiz::Char::Type CloseAngle = '>';
            /// brace {}
            static const ::Wiz::Char::Type OpenBrace = '{';
            static const ::Wiz::Char::Type CloseBrace = '}';

            ::Wiz::Size::Type OSZ = PS.BracketStack.Size();

            /// []
            if (CurrChar == OpenBracket)
            {
                if ((PS.RecordState == eRecording) && (OSZ == 0))
                {
                    PS.RecordState = eRecordEnd;
                }
                PS.BracketStack.Push(OpenBracket);
            }
            else if (CurrChar == CloseBracket && OSZ > 0)
            {
                if (PS.BracketStack.Top() != OpenBracket)
                {
                    return ::Wiz::Bool::False;
                }

                PS.BracketStack.Pop();
                if (PS.BracketStack.Size() == 0)
                {
                    PS.RecordState = eRecordEnd;
                }
            } /// ()
            else if (CurrChar == OpenParen)
            {
                if ((PS.RecordState == eRecording) && (OSZ == 0))
                {
                    PS.RecordState = eRecordEnd;
                }
                PS.BracketStack.Push(OpenParen);
            }
            else if (CurrChar == CloseParen && OSZ > 0)
            {
                if (PS.BracketStack.Top() != OpenParen)
                {
                    return ::Wiz::Bool::False;
                }

                PS.BracketStack.Pop();
                if (PS.BracketStack.Size() == 0)
                {
                    PS.RecordState = eRecordEnd;
                }
            } /// <>
            else if (CurrChar == OpenAngle)
            {
                if ((PS.RecordState == eRecording) && (OSZ == 0))
                {
                    PS.RecordState = eRecordEnd;
                }
                PS.BracketStack.Push(OpenAngle);
            }
            else if (CurrChar == CloseAngle && OSZ > 0)
            {
                if (PS.BracketStack.Top() != OpenAngle)
                {
                    return ::Wiz::Bool::False;
                }

                PS.BracketStack.Pop();
                if (PS.BracketStack.Size() == 0)
                {
                    PS.RecordState = eRecordEnd;
                }
            } /// {}
            else if (CurrChar == OpenBrace)
            {
                if ((PS.RecordState == eRecording) && (OSZ == 0))
                {
                    PS.RecordState = eRecordEnd;
                }
                PS.BracketStack.Push(OpenBrace);
            }
            else if (CurrChar == CloseBrace && OSZ > 0)
            {
                if (PS.BracketStack.Top() != OpenBrace)
                {
                    return ::Wiz::Bool::False;
                }

                PS.BracketStack.Pop();
                if (PS.BracketStack.Size() == 0)
                {
                    PS.RecordState = eRecordEnd;
                }
            }

            return PS.BracketStack.Size() > 0;
        }

        ::Wiz::Bool::Type Type::DoParse(::Wiz::Tokenizer::Result::Out Result, ::Wiz::Char::ConstPtr Str, ::Wiz::Size::In Size)
        {
            ::Wiz::Size::Type           CurrPos = 0;
            ::Wiz::Size::Type           StartPos = 0;
            register ::Wiz::Char::Type  CurrChar;

            ParseState                  PS;
            PS.IsInQuote = ::Wiz::Bool::False;
            PS.RecordState = eRecordBegin;

            for (; CurrPos < Size; CurrPos++)
            {
                if (PS.RecordState == eRecordBegin && PS.IsInQuote == ::Wiz::Bool::False)
                {
                    while (
                        (::Wiz::Bit::IsAllSet(m_Flags, eDefaultSeparators) && ::Wiz::IsSpace(Str[CurrPos])) ||
                        (::Wiz::Bit::IsAllSet(m_Flags, eSeparators) && IsSeparator(Str[CurrPos])) &&
                        CurrPos < Size)
                    {
                        CurrPos++;
                    }
                }
                CurrChar = Str[CurrPos];

                if (PS.RecordState == eRecordBegin)
                {
                    PS.RecordState = eRecording;
                    StartPos = CurrPos;
                }

                if (::Wiz::Bit::IsAllSet(m_Flags, eQuote) && ProcessQuote(CurrChar, PS, CurrPos))
                {
                }
                else if (::Wiz::Bit::IsAllSet(m_Flags, eBrackets) && ProcessBracket(CurrChar, PS, CurrPos))
                {
                }
                else if (::Wiz::Bit::IsAllSet(m_Flags, eSeparators) && ProcessSeparator(CurrChar, PS, CurrPos))
                {
                }
                else if (::Wiz::Bit::IsAllSet(m_Flags, eDefaultSeparators) && ProcessDefaultSeparator(CurrChar, PS, CurrPos))
                {
                }

                if (PS.RecordState == eRecordEnd || CurrPos + 1 == Size)
                {
                    PS.RecordState = eRecordBegin;

                    Result.AddArgument(Str, StartPos, CurrPos);
                }
            }
            return ::Wiz::Bool::True;
        }
    } /// end of namespace Tokenizer
} /// end of namespace Wiz


